--[[
    @Author       : baidwwy
    @Date         : 2021-02-11 11:49:09
    @LastEditTime : 2021-03-09 04:05:49
--]]

local _ENV = require("GGE界面")

--[[
按输入模式限制输入
]]
local function _检查输入内容(self,c,v)
    if not c then
        return false
    end

    --密码模式任意输入
    if self._模式&self.密码模式 == self.密码模式 then
        return true
    end

    if c>=48 and c<=57 then
        return self._模式&self.数字模式==self.数字模式
    elseif c>=65 and c<=90 then--大字字母
        return self._模式&self.英文模式==self.英文模式
    elseif c>=97 and c<=122 then--小字字母
        return self._模式&self.英文模式==self.英文模式
    elseif c==46 then--小数点
        return self._模式&self.小数模式==self.小数模式
    elseif c==45 then--负号
        return self._模式&self.负数模式==self.负数模式
    elseif #v==1 then
        return self._模式&self.符号模式==self.符号模式
    elseif #v>=3 then
        return self._模式&self.中文模式==self.中文模式
    end

    return false
end
--[[
计算光标位置前面字符总宽度
返回光标偏移
]]
local function _位置算光标(self,pos)
    local len = 0
    for i,v in ipairs(self._内容) do
        if i==pos then
            return len+self._显示偏移
        else
            len = len+self._文字:取宽度(v)
        end
    end
    return len+self._显示偏移
end
--[[
鼠标点击时,计算所在位置和光标位置前面字符总宽度
返回光标位置和光标偏移
]]
local function 宽度算光标(self,w)
    local len,a,b = 0
    for i,v in ipairs(self._内容) do
        local vw = self._文字:取宽度(v)
        if len+vw>=w then
            self._光标可见  = true
            if len+vw//2>w then
                a = i
                b = len+self._显示偏移
            else
                a = i+1
                b = len+vw+self._显示偏移
            end
            if b<0 then
                a = i+1
                b = len+vw+self._显示偏移
            elseif b>self.宽度 then
                a = i
                b = len+self._显示偏移
            end
            return a,b
        else
            len = len+vw
        end
    end
    return #self._内容+1,len+self._显示偏移
end
--[[
当输入或切换光标位置时，重新生成文本精灵和偏移
]]
local function _更新文本精灵(self)
    local str = self:取文本()
    if self._模式&self.密码模式==self.密码模式 then
        self._文本精灵 = self._文字:取精灵(string.rep(self._密码符号, #self._内容))
    else
        self._文本精灵 = self._文字:取精灵(str)
    end
    
    self._光标偏移 = _位置算光标(self,self._光标位置)

    if self._光标偏移<=2 then
        self._显示偏移 = self._显示偏移-self._光标偏移
        self._光标偏移 = 0
        if math.abs(self._显示偏移)>self.宽度/2 then
            local hw = self.宽度//2
            self._显示偏移 = self._显示偏移 + hw
            self._光标偏移 = self._光标偏移 + hw
        else
            self._光标偏移 = self._光标偏移 + math.abs(self._显示偏移)
            self._显示偏移 = 0
        end
    elseif self._光标偏移>=self.宽度 then
        local len,hw = 0,self.宽度//2
        local aux = ipairs(self._内容)
        for i,v in aux,self._内容,self._光标位置-1 do
            local vw = self._文字:取宽度(v)
            if len+vw>=hw then
                vw = len+vw
                self._显示偏移 = self._显示偏移 - vw
                self._光标偏移 = self._光标偏移 - vw
                return
            else
                len = len+vw
            end
        end
        len=len+(self._光标偏移-self.宽度)
        self._显示偏移 = self._显示偏移 - len
        self._光标偏移 = self._光标偏移 - len
    end
    self:发送消息("输入事件",str)
end
--[[
拖选时计算选中精灵宽度
]]
local function _两点算宽度(self,a,b)
    local len,t = 0,self._内容
    for i=a,b,(a<b and 1 or -1) do
        if t[i] then
            len = len+self._文字:取宽度(t[i])
        end
    end
    return len
end

local function _更新选中精灵(self)
    if self._按下位置 then
        if self._按下位置>1 then
            self._选中精灵:置中心(-_两点算宽度(self,1,self._按下位置-1),0)
        else
            self._选中精灵:置中心(0,0)
        end
        if self._按下位置==self._光标位置 then
            self._选中精灵:置区域(0,0,0,self.高度)
        elseif self._按下位置>self._光标位置 then
            self._选中精灵:置区域(0,0,-_两点算宽度(self,self._按下位置-1,self._光标位置),self.高度)
        else
            self._选中精灵:置区域(0,0,_两点算宽度(self,self._按下位置,self._光标位置-1),self.高度)
        end
    end
end


GUI输入 = class("GUI输入",GUI控件)
GUI输入._type  = 2
GUI输入.英文模式 = 1
GUI输入.数字模式 = 2
GUI输入.小数模式 = 4
GUI输入.负数模式 = 8
GUI输入.符号模式 = 16
GUI输入.中文模式 = 32
GUI输入.密码模式 = 64
GUI输入.正常模式 = 63
function GUI输入:初始化()
    self._文字 = require("SDL.文字")('simsun.ttc',self.高度)
    self._光标精灵 = require("SDL.精灵")(0,0,0,1,self.高度)
    self._选中精灵 = require("SDL.精灵")(0,0,0,0,self.高度):置颜色(0,120,215,255)
    
    self._闪烁间隔 = 0.6
    self._闪烁计时 = 0
    self._光标可见 = false 

    self._限制字数 = 0
    self._限制宽度 = 0

    self._光标偏移 = 0
    self._光标位置 = 1 
    self._显示偏移 = 0

    self._输入焦点 = false
    self._输入模式 = 0
    self._密码符号 = "*"
    self._禁止内容 = {}

    self._模式 = self.正常模式
    self:清空()
end

function GUI输入:_更新(dt)
    GUI控件._更新(self,dt)
    if not self.是否禁止 and self._输入焦点 then--光标闪烁计算
        if self._光标精灵.更新 then
            self._光标精灵:更新(dt)
        else
            self._闪烁计时 = self._闪烁计时 +dt
            if self._闪烁计时 >= self._闪烁间隔 then
                self._闪烁计时  = 0
                self._光标可见 = not self._光标可见
            end
        end
    end 
end

function GUI输入:_显示(...)
    GUI控件._显示(self,...)
    local _x,_y = self:取坐标()
    窗口:置区域(_x,_y,self.宽度,self.高度)
        self._选中精灵:显示(_x+self._显示偏移,_y)
        self._文本精灵:显示(_x+self._显示偏移,_y)
    窗口:置区域()

    if self._光标可见 then
        _x = _x+self._光标偏移
        self._光标精灵:显示(_x,_y)
        if self._ix~=_x or self._iy~=_y then
            self._ix = _x
            self._iy = _y
            SDL.SetTextInputRect(_x,_y+self.高度,0,0)
        end
    end
end
--===========================================================================
--配置相关
--===========================================================================
function GUI输入:置颜色(r,g,b,a)
    self._文字:置颜色(r,g,b,a)
    self._光标精灵:置颜色(r,g,b,a)
    return self
end

function GUI输入:取文字() 
    return self._文字
end

function GUI输入:置光标精灵(v)
    self._光标精灵 = v
    return self
end

function GUI输入:取光标精灵()
    return self._光标精灵
end

function GUI输入:置选中精灵(v)
    self._选中精灵 = v
    return self
end

function GUI输入:取选中精灵()
    return self._选中精灵
end

function GUI输入:置提示文本(内容,颜色)
    -- self._提示文本     = 内容
    -- self._提示文本颜色 = 颜色 or 0xFFA8A8A8
    return self
end

function GUI输入:取提示文本()
    -- return self._提示文本,self._提示文本颜色
end

function GUI输入:置模式(模式,符号)
    self._模式 = 模式
    if 符号 then
        self._密码符号 = 符号
    end
    return self
end

function GUI输入:置限制字数(v)
    self._限制字数 = v
    return self
end

function GUI输入:置限制宽度(v)
    self._限制宽度 = v
    return self
end
--===========================================================================
--内容相关
--===========================================================================
function GUI输入:清空()
    self._内容     = {}--记录字符

    self._光标偏移 = 0
    self._光标位置 = 1
    self._显示偏移 = 0
    self._文本精灵 = require("SDL.精灵")(0,0,0,0,0)
    self:取消选中()
end

function GUI输入:置数值(v)
    if type(v)=='number' then
        self:置文本(tostring(v))
    end
end

function GUI输入:插入文本(str,p,光标)
    
    if type(str)=='string' then
        --检查输入
        local t = {}
        for _,c in utf8.codes(str) do 
            local v = utf8.char(c)
            if _检查输入内容(self,c,v) then
                table.insert(t, v)
            else
                return false
            end
        end

        self:删除选中()
        --光标位置越界检查
        p = p or self._光标位置
        if p>#self._内容+1 then
            p = #self._内容+1
        elseif p<1 then
            p = 1
        end
        
        local len = self._文本精灵.宽度
        for i,v in ipairs(t) do
            if self._限制宽度>0 then
                len = len + self._文字:取宽度(v)
                if len >self._限制宽度 then
                    if i==1 then
                        return false
                    else
                        break
                    end
                end
            end
            if self._限制字数>0 then
                if #self._内容+1>self._限制字数 then
                    if i==1 then
                        return false
                    else
                        break
                    end
                end
            end
            table.insert(self._内容,p,v)
            p=p+1
        end
        self._光标位置 = p
        _更新文本精灵(self)
        return true
    end
end

function GUI输入:置文本(v)
    self:清空()
    self:插入文本(tostring(v),1,true)
    return self
end

function GUI输入:取文本()
    return table.concat(self._内容) or ''
end

function GUI输入:取数值()
    return tonumber(self:取文本()) or 0
end

function GUI输入:取内容()
    if self._模式~=self.正常模式 and self._模式&self.数字模式==self.数字模式 then
        return self:取数值()
    end
    return self:取文本()
end
--===========================================================================
--控制相关
--===========================================================================
function GUI输入:置焦点(v)
    if self._输入焦点 ~= v then
        self._输入焦点 = v
        if v then
            if _当前输入焦点 and _当前输入焦点 ~= self then
                _当前输入焦点:置焦点(false)
            end
            self._光标可见  = true
            _当前输入焦点 = self
            --self:_子消息事件('获得输入焦点')
        else
            self._光标可见  = false
            self:取消选中()
            --self:_子消息事件('失去输入焦点')
        end

        local platform = SDL.GetPlatform()
        if platform == 'Android' or platform == 'iOS' then
            if v then
                SDL.StartTextInput()
            else
                SDL.StopTextInput()
            end
        end

    end
    return self
end

function GUI输入:选中全部()
    self._按下位置 = 1
    self._光标位置 = #self._内容+1
    _更新选中精灵(self)
    return self
end

function GUI输入:删除选中()--删除选中的文本
    if self:是否选中() then
        if self._按下位置>self._光标位置 then
            for i=1,self._按下位置-self._光标位置 do
                table.remove(self._内容,self._光标位置)
            end
        else
            for i=1,self._光标位置-self._按下位置 do
                table.remove(self._内容,self._按下位置)
            end
            self._光标位置 = self._按下位置
        end
        
        _更新文本精灵(self)
        self:取消选中()
        return true
    end
end

function GUI输入:是否选中()
    return self._选中精灵.区域宽度~=0
end

function GUI输入:取选中()
    if self:是否选中() then
        if self._按下位置>self._光标位置 then
            return table.concat( self._内容, "", self._光标位置, self._按下位置-1 )
        end
        return table.concat( self._内容, "", self._按下位置, self._光标位置-1 )
    end
    return ''
end

function GUI输入:取消选中()
    self._按下位置 = nil
    if self:是否选中() then
        self._选中精灵:置区域(0,0,0,self.高度)
        return true
    end 
end

function GUI输入:置禁止(v)
    self.是否禁止 = v
    self._光标可见 = false
    return self
end
--===========================================================================

function GUI输入:_消息事件(msg)
    
    if self._输入焦点 and not self.是否禁止 then
        for _,v in ipairs(msg.输入) do
            self:插入文本(v.text,nil,true)
        end
    end

    -- if #msg.鼠标>0 then
    --     self:发送消息("鼠标事件",table.unpack(msg))
    -- end
    for _,v in ipairs(msg.鼠标) do
        if v.type==SDL.鼠标_按下 then
            if v.button==SDL.BUTTON_LEFT then
                if self:检查点(v.x,v.y) then
                    v.type = nil
                    self:置焦点(true)
                    self._光标位置,self._光标偏移 = 宽度算光标(self,math.abs(self._显示偏移)+(v.x-self.x))
                    self._按下位置 = self._光标位置
                else
                    self:取消选中()
                end
            end
        elseif v.type==SDL.鼠标_弹起 then
            if v.button==SDL.BUTTON_LEFT then
                if self:检查点(v.x,v.y) then
                    v.type = nil
                    if self._光标位置==self._按下位置 then
                        self:取消选中()
                    end
                    if v.clicks==2 then--双击全选
                        self:选中全部()
                    end
                else
                    self:置焦点(false)
                end
            end
        elseif v.type==SDL.鼠标_移动 then--拖选
            if self:检查点(v.x,v.y) then
                self._focus = true
                local x,y = self:取坐标()
                self:发送消息("获得鼠标",x,y,msg)
            elseif self._focus then
                self._focus = nil
                self:发送消息("失去鼠标",v.x,v.y,msg)
            end
            
            if self._按下位置 and v.state==SDL.BUTTON_LMASK then--左键按住
                v.type = nil
                self._光标位置,self._光标偏移 = 宽度算光标(self,math.abs(self._显示偏移)+(v.x-self.x))
                _更新选中精灵(self)
                _更新文本精灵(self)
            end
        end
    end
    

    if self._输入焦点 then
        for _,v in ipairs(msg.键盘) do
            if self:发送消息("键盘事件",table.unpack(v)) then
                v[2] = nil
            end
            if v.state then--按下
                if v.keysym.mod&SDL.KMOD_CTRL==SDL.KMOD_CTRL then
                    if v.keysym.sym==SDL.KEY_C then--复制
                        if self:是否选中() then
                            SDL.SetClipboardText(self:取选中())
                        end
                    elseif v.keysym.sym==SDL.KEY_A then--全选
                        self:选中全部()
                    elseif v.keysym.sym==SDL.KEY_V then--粘贴
                        if SDL.HasClipboardText() then
                            self:删除选中()
                            self:插入文本(SDL.GetClipboardText(),self._光标位置,true)
                        end
                    elseif v.keysym.sym==SDL.KEY_X then--剪贴
                        if self:是否选中() then
                            SDL.SetClipboardText(self:取选中())
                            self:删除选中()
                        end
                    elseif v.keysym.sym==SDL.KEY_Z then--撤消

                    end
                elseif v.keysym.mod&SDL.KMOD_SHIFT==SDL.KMOD_SHIFT then
                    if v.keysym.sym==SDL.KEY_LEFT then--左选中
                        if self._按下位置 then
                            if self._光标位置>1 then
                                self._光标位置 = self._光标位置-1
                            end
                        else
                            self._按下位置 = self._光标位置
                            self._光标位置 = self._光标位置-1
                        end
                        if self._光标位置==self._按下位置 then
                            self:取消选中()
                        end
                        _更新选中精灵(self)
                        _更新文本精灵(self)
                    elseif v.keysym.sym==SDL.KEY_RIGHT then--右选中
                        if self._按下位置 then
                            if self._光标位置<=#self._内容 then
                                self._光标位置 = self._光标位置+1
                            end
                        else
                            self._按下位置 = self._光标位置
                            self._光标位置 = self._光标位置+1
                        end
                        if self._光标位置==self._按下位置 then
                            self:取消选中()
                        end
                        _更新选中精灵(self)
                        _更新文本精灵(self)
                    elseif v.keysym.sym==SDL.KEY_HOME then--选中最左
                        self._按下位置 = self._光标位置
                        self._光标位置 = 1
                        _更新选中精灵(self)
                        _更新文本精灵(self)
                    elseif v.keysym.sym==SDL.KEY_END then--选中最右
                        self._按下位置 = self._光标位置
                        self._光标位置 = #self._内容+1
                        _更新选中精灵(self)
                        _更新文本精灵(self)
                    end
                elseif v.keysym.sym==SDL.KEY_BACKSPACE then--退格
                    if not self:删除选中() then
                        if self._光标位置 > 1 then
                            table.remove(self._内容,self._光标位置-1)
                            self._光标位置 = self._光标位置 -1
                            self._光标可见 = true
                            _更新文本精灵(self)
                        end
                    end
                elseif v.keysym.sym==SDL.KEY_LEFT then--左移动
                    if self:是否选中() then
                        if self._按下位置<self._光标位置 then
                            self._光标位置 = self._按下位置
                            _更新文本精灵(self)
                        end
                        self:取消选中()
                    elseif self._光标位置 > 1 then
                        self._光标位置 = self._光标位置 -1
                        self._光标可见 = true
                        _更新文本精灵(self)
                    end
                elseif v.keysym.sym==SDL.KEY_RIGHT then--右移动
                    if self:是否选中() then
                        if self._按下位置>self._光标位置 then
                            self._光标位置 = self._按下位置
                            _更新文本精灵(self)
                        end
                        self:取消选中()
                    elseif self._光标位置 <= #self._内容 then
                        self._光标位置 = self._光标位置 +1
                        self._光标可见 = true
                        _更新文本精灵(self)
                    end
                elseif v.keysym.sym==SDL.KEY_HOME then--移动最左
                    self:取消选中()
                    self._光标位置 = 1
                    _更新文本精灵(self)
                elseif v.keysym.sym==SDL.KEY_END then--移动最右
                    self:取消选中()
                    self._光标位置 = #self._内容+1
                    _更新文本精灵(self)
                end
            end
        end
    end
end
return GUI输入